#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>

namespace my_blockqueue
{
    int s_cap = 5;
    template<class T>
    class BlockQueue
    {
    private:
        std::queue<T> _bq;//阻塞队列
        int _cap;//队列的元素上限
        pthread_mutex_t _mtx;//保护临界资源的锁
        pthread_cond_t _is_full;//_bq满的，生产者在该条件变量下等待
        pthread_cond_t _is_empty;//_bq空的，消费者者在该条件变量下等待

    public:
        BlockQueue():_cap(s_cap)
        {
            pthread_mutex_init(&_mtx, nullptr);//初始化锁
            pthread_cond_init(&_is_full, nullptr);//初始化条件变量，第二个参数先传nullptr
            pthread_cond_init(&_is_empty, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);//释放锁
            pthread_cond_destroy(&_is_full);//销毁条件变量
            pthread_cond_destroy(&_is_empty);
        }

        //生产
        void Push(const T& data)
        {
            pthread_mutex_lock(&_mtx);//加锁
            //循环判断是否满了，满了就wait
            while(IsFull())
            {
                pthread_cond_wait(&_is_full, &_mtx);
                //调用的时候会先自动释放锁，然后再挂起等待
                //返回的时候会先自动竞争锁，获取到锁后才能返回
                pthread_cond_signal(&_is_empty);//唤醒消费者
            }
            //插入数据
            _bq.push(data);
            //判断唤醒消费者
            if(_bq.size() > _cap/2) 
            {
                pthread_cond_signal(&_is_empty);
            }
            // std::cout << "Push a data:" << data << std::endl;
            pthread_mutex_unlock(&_mtx);//解锁
        }
        //消费
        void Pop(T* data)//data是输出型参数，让消费者拿到任务(数据)
        {
            pthread_mutex_lock(&_mtx);//加锁
            //循环判断是否空了，空了就wait
            while(IsEmpty())
            {
                pthread_cond_wait(&_is_empty, &_mtx);
                pthread_cond_signal(&_is_full);//唤醒生产者
            }
            //删除数据
            *data = _bq.front();//让消费者拿到任务(数据)
            _bq.pop();
            //判断唤醒生产者
            if(_bq.size() <= _cap/2) 
            {
                pthread_cond_signal(&_is_full);
            }
            // std::cout << "Pop a data:" << *data << std::endl;
            pthread_mutex_unlock(&_mtx);//解锁
        }

        bool IsFull()
        {
            return _bq.size() == _cap;
        }
        bool IsEmpty()
        {
            return _bq.size() == 0;
        }
    };
}
